const Util = require('./commonJs');
const axios = require('axios');
const mysql = require('promise-mysql');
const config = require('./server-config');

let httpHost = config.SDK;

let sql = {
    createTable:{
	block:"create table exp_block (" +
	"height BIGINT," +
	"create_time BIGINT," +
	"block_hash varchar(255)  NOT NULL PRIMARY KEY," +
	"previous_hash TEXT," +
	"world_state_root TEXT," +
	"transaction_root TEXT," +
	"tx_count BIGINT," +
	"tx_list TEXT" +
	")",
	transaction:"create table exp_transaction (" +
	"transaction_hash varchar(255) NOT NULL PRIMARY KEY," +
	"block_height BIGINT," +
	"smart_contract TEXT," +
	"create_time BIGINT" +
	")"
    },
    clearData:{
	block:"truncate table exp_block",
	transaction:"truncate table exp_transaction",
    },
    insert:{
	block:(height,create_time,block_hash,previous_hash,world_state_root,transaction_root,tx_count,tx_list)=>{
	    return `insert into exp_block(height,create_time,block_hash,previous_hash,world_state_root,transaction_root,tx_count,tx_list) values(${height},${create_time},'${block_hash}','${previous_hash}','${world_state_root}','${transaction_root}',${tx_count},'${tx_list}')`;
	},
	transaction:(block_height,smart_contract,transaction_hash,create_time)=>{
	    return `insert into exp_transaction(block_height,smart_contract,transaction_hash,create_time) values(${block_height},'${smart_contract}','${transaction_hash}',${create_time})`;
	},
    }
};

/***
 * 通过 SDK 返回的数据格式 重组并生成sql
 * @param data
 * @returns {*|string}
 */
let makeSql = {
    insertBlock:(data)=>{
	const {
	    header:{
		height = 0,
		timestamp = 0,
		blockHash = "",
		previousHash = "",
		worldStateRoot = "",
		transactionRoot = "",
	    },
	    txs = []
	} = data;
	return sql.insert.block(
	    height,
	    timestamp,
	    blockHash,
	    previousHash,
	    worldStateRoot,
	    transactionRoot,
	    txs.length,
	    JSON.stringify(txs)
	)
    },
    insertTx:(data)=>{

    }
};


let mysql_pool = mysql.createPool(config.mysql);

let axiosCake = (para)=>{
    let option = Object.assign({
	method:"get",
	url:"",
	data:{}
    },para);
    return new Promise((success,error)=>{
	if(!Util.isEmpty(option.url)){
	    axios(option).then((data)=>{
		if(data.data.State === 200){
		    success({
			code:200,
			data:data.data.Data,
			msg:data.data.Message
		    })
		}else{
		    success({
			code:data.data.State,
			data:data.data.Data,
			msg:data.data.Message
		    });
		    console.log(`${option.method} data >>> ${option.data} url>>> ${option.url}`);
		    try{
			console.log(`${Util.timeFormat(new Date(),"yyyy-MM-dd hh:mm:ss")}>> Message::::${JSON.stringify(data.data)}`);
		    } catch (e){
			console.log("返回参数有误，疑似服务器关闭");
		    }
		}
	    },(err)=>{
		success({
		    code:400,
		    data:"",
		    msg:err
		});
		console.log(`${option.method} data >>> ${option.data} url>>> ${option.url}`);
		console.log(`Error Message::::${err}`);
	    })
	}else {
	    success({
		code:400,
		data:"",
		msg:"url为空"
	    });
	}
    })
};

let HTTP_SDK = {
    path:{
	createTx:{
	    url:"/createnewtransaction",
	    method:"post"
	},
	getTxByHash:{
	    url:"/gettransaction",
	    method:"get"
	},
	getTxIndexByHash:{
	    url:"/gettransactionindex",
	    method:"get"
	},
	getHighByTxHash:{
	    url:"/gettransactionblock",
	    method:"get"
	},
	getHigh:{
	    url:"/getheight",
	    method:"get"
	},
	getBlockByHigh:{
	    url:"/getblockbyheight",
	    method:"get"
	},
	getBlockByHash:{
	    url:"/getblockbyhash",
	    method:"get"
	},
    },
    returnOption:(para)=>{
	return{
	    code:Util.isEmpty(para.code)?200:para.code,
	    data:para.data,
	    msg:para.msg,
	}
    },
    /***
     * 通过交易哈希获取交易详细信息
     * @param hex
     * @returns {Promise}
     */
    getTransactionByHex:(hex)=>{
	return new Promise((success,error)=>{
	    axios({
		url:`${httpHost}${HTTP_SDK.path.getTxByHash.url}?hashData=${encodeURIComponent(hex)}`,
		method:HTTP_SDK.path.getTxByHash.method
	    }).then((data)=>{
		success(data.data);
	    });
	    /*axiosCake({
		url:`${httpHost}${HTTP_SDK.path.getTxByHash.url}?hashData=${encodeURIComponent(hex)}`,
		method:HTTP_SDK.path.getTxByHash.method
	    }).then((data)=>{
		if(data.code === 200){
		    success(HTTP_SDK.returnOption({
			data:{
			    smartContract:data.data.smartContract,
			    transactionHash:data.data.header.transactionHash,
			    createTime:data.data.header.timestamp*1000,
			    smartContractArgs:data.data.smartContractArgs
			}
		    }));
		}else{
		    success(HTTP_SDK.returnOption(data));
		    error(data);
		}
	    });*/
	})
    },
    /***
     * 通过高度获取区块详细信息
     * @param high
     * @returns {Promise}
     */
    getBlockDetailByHigh:(high)=>{
	return new Promise((success,error)=>{
	    axios({
		url:`${httpHost}${HTTP_SDK.path.getBlockByHigh.url}?number=${high}`,
		method:HTTP_SDK.path.getBlockByHigh.method
	    }).then((data)=>{
		success(data.data);
	    },(err)=>{
	        console.log("通过高度获取区块详情出错:::>",err)
	    });
	})
    },
    /***
     * 获取区块高度
     * @returns {Promise}
     */
    getBlockHigh:()=>{
	return new Promise((success,error)=>{
	    axios({
		url:`${httpHost}${HTTP_SDK.path.getHigh.url}`,
		method:HTTP_SDK.path.getBlockByHigh.method
	    }).then((data)=>{
		success(data.data);
	    },err=>{
		error();
	        console.error("SDK服务已终止")
	    });
	})
    }
};
let HTTP_Control = {
    getPeerList:()=>{
	return new Promise((suc,err)=>{
	    axiosCake({
		url:`${httpHost}/getpeerlist`
	    }).then((data)=>{
		suc(data.data);
	    });
	})
    },
    getMemory:(ip)=>{
	return new Promise((suc,err)=>{
	    axiosCake({
		url:`${httpHost}/getmemory?ip=${ip}`,
	    }).then((data)=>{
		suc(data.data);
	    });
	})
    },
    getDisk:(ip)=>{
	return new Promise((suc,err)=>{
	    axiosCake({
		url:`${httpHost}/getdisk?ip=${ip}`,
	    }).then((data)=>{
		suc(data.data);
	    });
	})
    },
    getCPU:(ip)=>{
	return new Promise((suc,err)=>{
	    axiosCake({
		url:`${httpHost}/getcpu?ip=${ip}`,
	    }).then((data)=>{
		suc(data.data);
	    });
	})
    },
    getNetwork:(ip)=>{
	return new Promise((suc,err)=>{
	    axiosCake({
		url:`${httpHost}/getnetwork?ip=${ip}`,
	    }).then((data)=>{
		suc(data.data);
	    });
	})
    },
};

/***
 * 初始化表
 * @type {function()}
 */
module.exports.initDataBase = ()=>{
    return mysql_pool.query("SELECT table_name FROM information_schema.TABLES WHERE table_name ='exp_block'").then((rows)=>{
        return new Promise((success,error)=>{
	    if(rows.length === 0){
		return mysql_pool.query(sql.createTable.block).then(()=>{
		    console.log("create table block success");
		    success();
		})
	    }else{
		success();
	    }
	});
    }).then(()=>{
	return new Promise((success,error)=>{
	    mysql_pool.query("SELECT table_name FROM information_schema.TABLES WHERE table_name ='exp_transaction'").then((rows)=>{
		if(rows.length === 0){
		    return mysql_pool.query(sql.createTable.transaction).then((rows)=>{
			console.log("create table transaction success");
			success();
		    });
		}else{
		    success();
		}
	    });
	})
    })
};
/***
 * 获取按时间倒序的块列表
 * @param count 指定条数
 * @returns {Promise}
 */
module.exports.getBlockByShowCount = (count)=>{
    return new Promise((suc,err)=>{
	mysql_pool.query(`select * from exp_block ORDER BY height desc limit ${count} OFFSET 0 `,(err,rows)=>{
	    if(!err){
		suc(rows);
	    }else{
		suc("");
	    }
	});
    })
};
/***
 * 获取块高度
 * @returns {Promise}
 */
module.exports.getBlockHigh = ()=>{
    return new Promise((suc,err)=>{
	// mysql_pool.query(`SELECT count(*) as count FROM exp_block `,(err,rows)=>{
	mysql_pool.query(`select max(height) as max from exp_block`,(err,rows)=>{
	    if(!err){
		suc(rows[0]);
	    }else{
		suc("");
	    }
	});
    })
};
/***
 * 获取交易列表 new !
 * @param count
 * @returns {Promise}
 */
module.exports.getOverviewTxList = (count = 6)=>{
    return HTTP_SDK.getBlockHigh().then((data)=>{
	const { Data , State , Message } = data;
        return new Promise((success,error)=>{
	    let txHexList = [];
	    let getList = (high)=>{
	        if(high > -1){
		    HTTP_SDK.getBlockDetailByHigh(high).then(($data)=>{
			const { Data:{ txs } } = $data;
			txHexList = [...txHexList,...txs];
			if(txHexList.length < count){
			    getList(high-1)
			}else{
			    success(txHexList.slice(0,count));
			}
		    })
		}else{
		    success(txHexList.slice(0,count));
		}
	    };
	    getList(Data-1);
	})
    }).then((txHexList)=>{
        return new Promise((success,error)=>{
	    let txDetailList = [];
	    let getTxDetail = (index)=>{
		if(index < 0){
		    success(txDetailList.reverse());
		}else{
		    HTTP_SDK.getTransactionByHex(txHexList[index]).then((data)=>{
		        if(data.State === 200){
			    const {
				header:{
				    version,
				    timestamp,
				    transactionHash,
				},
				smartContract,
				smartContractArgs,
				code,
				message,
				writes,
				contractResult,
				txRecords,
			    } = data.Data;
			    txDetailList.push({
				smartContract:smartContract,
				transactionHash:transactionHash,
				createTime:timestamp*1000,
				smartContractArgs:smartContractArgs,
				version:version,
				code:code,
				message:message,
				writes:writes,
				contractResult:contractResult,
				txRecords:txRecords
			    });
			    getTxDetail(index-1);
			}
		    });
		}
	    };
	    getTxDetail(txHexList.length-1);
	})
    })
};
/***
 * === 已废弃 ===
 * 获取交易列表
 * @param count
 * @returns {Promise}
 */
module.exports.getTxList = (count = 6)=>{
    return new Promise((suc,err)=>{
	let txHexList = [];
	mysql_pool.query(`select height from exp_block ORDER BY create_time desc limit ${count} OFFSET 0 `,(err,rows)=>{
	    // ### 此处获取 前(count//默认为6个) 并请求获取这些块中的所有交易集合 提取前count个数量并返回
	    return new Promise(($suc,$err)=>{
		let arr = [];
		let aaa = (index,data)=>{
		    HTTP_SDK.getBlockDetailByHigh(data[index].height).then(($data)=>{
			for(let i=0;i<$data.data.txList.length;i++){
			    arr.push($data.data.txList[i]);
			    if(arr.length === count || ((index+1) === rows.length)){
				$suc(arr);
				return false;
			    }else{
				if((i+1) === $data.data.txList.length){
				    aaa(index+1,data);
				}
			    }
			}
		    })
		};
		aaa(0,rows);
	    }).then(($txHexList)=>{
		let txList = [];
		if(!Util.isEmpty($txHexList)){
		    $txHexList.map((n,i)=>{
			HTTP_SDK.getTransactionByHex(n).then((data)=>{
			    if(data.code === 200){
				txList.push(data.data);
				if(txList.length === $txHexList.length){
				    suc(txList);
				}
			    }
			})
		    });
		}else{
		    suc(txList);
		}
	    })
	})
    })
};
/***
 * 通过交易hex获取交易详情
 * @param txHex
 * @returns {Promise}
 */
module.exports.getTxInfo = (txHex)=>{
    return new Promise((suc,err)=>{
	HTTP_SDK.getTransactionByHex(txHex).then((data)=>{
	    const { Data , State , Message } = data;
	    if(State === 200){
		suc({
		    smartContract:Data.smartContract,
		    transactionHash:Data.header.transactionHash,
		    createTime:Data.header.timestamp*1000,
		    smartContractArgs:Data.smartContractArgs,
		    version:Data.header.smartContractArgs,
		    code:Data.code,
		    message:Data.message,
		    writes:Data.writes,
		    contractResult:Data.contractResult,
		    txRecords:Data.txRecords
		});
	    }else{
		err(Message)
	    }
	},(err)=>{
	    err()
	})
    })
};
/***
 * 通过块高度获取块详情
 * @param blockHigh
 * @returns {Promise}
 */
module.exports.getBlockInfoByHigh = (blockHigh)=>{
    let h = "";
    try{
	h = parseInt(blockHigh);
    }catch (e){}
    return new Promise((suc,err)=>{
	mysql_pool.query(`select * from exp_block where height = ${h}`,(err,rows)=>{
	    if(!err){
		suc(rows[0]);
	    }else{
		suc("");
	    }
	});
    })
};
/***
 * 通过块hash度获取块详情
 * @param hex
 * @returns {Promise}
 */
module.exports.getBlockInfoByHex = (hex)=>{
    return new Promise((suc,err)=>{
	mysql_pool.query(`select * from exp_block where block_hash = '${hex.toString().trim()}'`,(err,rows)=>{
	    if(!err){
		suc(rows[0]);
	    }else{
		suc("");
	    }
	});
    })
};
/***
 * 本地数据库清除 block 表数据
 * @returns {Promise}
 */
module.exports.clearBlock = ()=>{
    return new Promise((suc,err)=>{
	mysql_pool.query(sql.clearData.block,(err,rows)=>{
	    if(!err){
		suc(rows[0]);
	    }else{
		suc("");
	    }
	});
    })
};
/***
 * 重载数据
 * @param fn
 */
module.exports.reloadData = (fn)=>{
    HTTP_SDK.getBlockHigh().then((data)=>{
	const { Data , State , Message } = data;
	let loadBlock = (high)=>{
	    HTTP_SDK.getBlockDetailByHigh(high).then(($data)=>{
		if($data.State === 200){
		    let _sql = makeSql.insertBlock($data.Data);
		    mysql_pool.query(_sql,(err,res)=>{
			!err?console.log(`block insert success height:${high}`):err;
			const nextHigh = high-1;
			if(nextHigh >= 0){
			    loadBlock(nextHigh);
			}else{
			    console.log(">>> block insert over <<<");
			    fn?fn():"";
			}
		    });
		}
	    })
	};
	loadBlock(Data-1)
    })
};
/***
 * 更新块信息
 * @returns {Promise}
 */
module.exports.updateBlock = ()=>{
    return new Promise((success,error)=>{
	mysql_pool.query("select max(height) as max from exp_block",(err,rows)=>{
	    if(!err){
		success(Util.isEmpty(rows[0].max)?0:rows[0].max);
	    }
	});
    }).then((max)=>{
	return new Promise((success,error)=>{
	    return HTTP_SDK.getBlockHigh().then((data)=>{
		const { Data , State , Message } = data;
		success({
		    min_high:max,
		    max_high:Data
		});
	    },()=>{
		error();
	    })
	})
    }).then((data)=>{
	const { min_high ,max_high } = data;
	return new Promise((success,error)=>{
	    let getList = (high)=>{
		return HTTP_SDK.getBlockDetailByHigh(high).then(($data)=>{
		    if($data.State === 200){
		        let _sql = makeSql.insertBlock($data.Data);
			mysql_pool.query(_sql,(err,res)=>{
			    !err?console.log(`--- block-on-update --> height:${high} ---`):"";
			    if(high > min_high){
				getList(high-1);
			    }else{
				success();
			    }
			});
		    }
		})
	    };
	    if(max_high > (min_high+1)){
		getList(max_high-1);
	    }else{
		console.log(`--- block-no-update --> height:${min_high} ---`);
		success();
	    }
	})
    },()=>{

    })
};

module.exports.getCpu = (ip)=>{
    return new Promise((suc,err)=>{
	HTTP_Control.getCPU(ip).then((data)=>{
	    setTimeout(()=>{
		HTTP_Control.getCPU(ip).then(($data)=>{
		    let total_old = data.Total;
		    let total_new = $data.Total;
		    let used_old = data.Used;
		    let used_new = $data.Used;
		    let aaa = (used_new-used_old)/(total_new-total_old);
		    suc(aaa);
		})
	    },1500)
	})
    })
};
module.exports.getNetwork = (ip)=>{
    return new Promise((suc,err)=>{
	HTTP_Control.getNetwork(ip).then((data)=>{
	    setTimeout(()=>{
		HTTP_Control.getNetwork(ip).then(($data)=>{
		    let old_Receive = data.Receive;
		    let old_Transmit = data.Transmit;
		    let new_Receive = $data.Receive;
		    let new_Transmit = $data.Transmit;
		    let aaa = (new_Receive-old_Receive)+(new_Transmit-old_Transmit);
		    suc(aaa);
		})
	    },1500)
	})
    })
};
module.exports.getPeerList = HTTP_Control.getPeerList;
module.exports.getMemory = HTTP_Control.getMemory;
module.exports.getDisk = HTTP_Control.getDisk;

